////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "Hull.hpp"

#include <SFML/OpenGL.hpp>

#include <SmegTools/Tools.hpp>

#include "../Light/LightSystem.hpp"
#include "../Entity/ShadowCasterEntity.hpp"

#include <SmegTools/Debug/DebugNew.hpp>

namespace smeg {
    namespace light {
        
////////////////////////////////////////////////////////////
/// Default Constructor
////////////////////////////////////////////////////////////
Hull::Hull( const sf::Vector2f& _Center, float _fAngleDeg, bool _bIsStatic, ShadowCasterEntity* _pParent ) :
    QuadTreeOccupant( _Center, _fAngleDeg, _bIsStatic ),
    m_uNumVertices( 0 ),
    m_pOriginalVertices( NULL ),
    m_pVertices( NULL ),
    m_pParent( _pParent )
{
    LightSystem::GetInstance()->AddHull( this );
}
    
////////////////////////////////////////////////////////////
/// Copy Constructor
////////////////////////////////////////////////////////////
Hull::Hull( const Hull& _Copy ) :
    QuadTreeOccupant( _Copy ),
    m_uNumVertices( _Copy.m_uNumVertices ),
    m_pOriginalVertices( NULL ),
    m_pVertices( NULL ),
    m_pParent( _Copy.m_pParent )
{
    if (m_uNumVertices > 0) {
        m_pOriginalVertices = new sf::Vector2f[m_uNumVertices];
        for (unsigned i = 0; i < m_uNumVertices; ++i) {
            m_pOriginalVertices[i] = _Copy.m_pOriginalVertices[i];
        }
        m_pVertices = new sf::Vector2f[m_uNumVertices];
        for (unsigned i = 0; i < m_uNumVertices; ++i) {
            m_pVertices[i] = _Copy.m_pVertices[i];
        }
    }
    LightSystem::GetInstance()->AddHull( this );
}
    
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
Hull::~Hull()
{
    Clear();

    // Nulify this in the parent if any so when the parent is destroyed it doesn't try to destroy the hull again
    if ( m_pParent ) {
        m_pParent->NullifyHull();
    }
    m_pParent = NULL;
}

////////////////////////////////////////////////////////////
/// Render the hull
////////////////////////////////////////////////////////////
void Hull::Render( const sf::Color& _Color ) const {
    // Setup position
    glPushMatrix();
    glPushAttrib( GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT );
    glTranslatef( m_Center.x, m_Center.y, 0.0f );
    glColor4ub( _Color.r, _Color.g, _Color.b, _Color.a );

    // Draw
    RenderVertices();
    
    // Reset position
    glPopAttrib();
    glPopMatrix();
}

////////////////////////////////////////////////////////////
/// Render the vertices
////////////////////////////////////////////////////////////
void Hull::RenderVertices() const {
    glBegin( GL_TRIANGLE_FAN );
    for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
        glVertex3f( m_pVertices[i].x, m_pVertices[i].y, 0.0f );
    }
    glEnd();
}

////////////////////////////////////////////////////////////
/// Compute the hull
////////////////////////////////////////////////////////////
void Hull::ComputeHull() {
    // TODO See if optimisation isn't possible...
    CenterHull();
    RotateVertices();
    ComputeAABB();
}

////////////////////////////////////////////////////////////
/// Center the hull
////////////////////////////////////////////////////////////
void Hull::CenterHull() {
    // Calculate the average of all of the vertices, and then
    // offset all of them to make this the new origin position
    sf::Vector2f offsetCenter( 0.0f, 0.0f );

    for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
        offsetCenter += m_pOriginalVertices[i];
    }

    offsetCenter /= static_cast<float>(m_uNumVertices);

    for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
        m_pOriginalVertices[i] -= offsetCenter;
    }
}

////////////////////////////////////////////////////////////
/// Rotate the vertices
////////////////////////////////////////////////////////////
void Hull::RotateVertices() {
    if (!m_pVertices) {
        m_pVertices = new sf::Vector2f[m_uNumVertices];
    }

    float cosAngle = cos(m_fAngleRad),
        sinAngle = sin(m_fAngleRad);

    for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
        m_pVertices[i].x = cosAngle * m_pOriginalVertices[i].x - sinAngle * m_pOriginalVertices[i].y;
        m_pVertices[i].y = sinAngle * m_pOriginalVertices[i].x + cosAngle * m_pOriginalVertices[i].y;
    }
}

////////////////////////////////////////////////////////////
/// Compute the AABB
////////////////////////////////////////////////////////////
void Hull::ComputeAABB() {
    sf::Vector2f lowerBound( m_pVertices[0] ),
        upperBound( lowerBound );
    sf::Vector2f* pCurVert;

    for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
        pCurVert = &m_pVertices[i];
        
        if ( pCurVert->x > upperBound.x )
            upperBound.x = pCurVert->x;
        if ( pCurVert->y > upperBound.y )
            upperBound.y = pCurVert->y;
        if ( pCurVert->x < lowerBound.x )
            lowerBound.x = pCurVert->x;
        if ( pCurVert->y < lowerBound.y )
            lowerBound.y = pCurVert->y;
    }
    m_AABB.SetBounds( lowerBound + m_Center, upperBound + m_Center );
    TreeUpdate();
}

////////////////////////////////////////////////////////////
/// Clear the hull of all of its vertices
////////////////////////////////////////////////////////////
void Hull::Clear() {
    m_uNumVertices = 0;

    delete[] m_pOriginalVertices;
    m_pOriginalVertices = NULL;
    delete[] m_pVertices;
    m_pVertices = NULL;
}

/*********************** Accessors ************************/
void Hull::SetAngleRad( float _fAngleRad ) {
    QuadTreeOccupant::SetAngleRad( _fAngleRad );
    
    ComputeHull();
}

void Hull::RotateRad( float _fAngleRad ) {
    QuadTreeOccupant::RotateRad( _fAngleRad );
    
    ComputeHull();
}

#ifdef _DEBUG
void Hull::DebugRender( const sf::Color& _Color ) const {
    if ( DEBUG_DebugRender ) {
        // Save position and color
        glPushMatrix();
        glPushAttrib( GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT );

        // Draw
        glTranslatef( m_Center.x, m_Center.y, 0.0f );
        glColor4ub( _Color.r, _Color.g, _Color.b, _Color.a );
        glBegin( GL_LINE_LOOP );
        for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
            glVertex2f( m_pVertices[i].x, m_pVertices[i].y );
        }
        glEnd();

        // Reset position and color
        glPopAttrib();
        glPopMatrix();
    }
}
#endif // _DEBUG

    } // namespace light
} // namespace smeg